<template>
  <div class="super-slider-wrapper" :id="id" :ref="id" :style="{ width: width + 'px' }">
    <div class="bar-container" refs="test">
      <div class="bar-into"></div>
    </div>
    <div
      v-for="(item, index) in bars"
      ref="barDrag"
      :key="index"
      :class="{ active: item.active, 'bar-drag': true }"
      :style="{ left: item.w + 'px' }"
    >
      <div class="bar-text">{{ item.x }}</div>
      <div class="color">
        <colorPicker v-model="item.color" @input="changeColor" />
      </div>
    </div>
  </div>
</template>
<script>
import vcolorpicker from 'vcolorpicker'
import { unique, isArray, sort } from './lib'

export default {
  name: 'SuperSlider',
  components: {
    vcolorpicker
  },
  props: {
    id: {
      type: String,
      default: 'super-slider'
    },
    max: {
      type: Number,
      default: 100
    },
    width: {
      type: Number,
      default: 300
    },
    changeSuperSlider: {
      type: Function
    },
    data: {
      type: Array,
      default: () => []
    },
    currentWidth: {
      type: Number,
      default: 12
    },
    step: {
      type: Number,
      default: 1
    }
  },
  data() {
    return {
      activedNum: 0,
      bars: [
        {
          w: 0,
          x: 0,
          active: true,
          color: '#ffffff'
        }
      ],
      defaultData: {
        active: false,
        color: '#ff0000'
      },
      barWrap: null,
      barCon: null,
      nMax: 0
    }
  },
  watch: {
    data() {
      this.setDfault()
    },
    max() {
      this.setDfault()
    }
  },
  computed: {
    pointNum() {
      const arr = this.step.toString().split('.')
      return arr[1] ? arr[1].length : 0
    }
  },
  mounted() {
    this.initData()
    this.dispatchMouseDown()
    this.dispatchDblclick()
    this.setDfault()
    document.addEventListener('keyup', this.dispatchOnKeyUP)
  },
  beforeDestroy() {
    this.barCon.removeEventListener('dblclick', this.handleDblClick)
    document.removeEventListener('keyup', this.dispatchOnKeyUP)
    const eBarDrags = this.$refs['barDrag']
    eBarDrags.forEach((item) => {
      item.onmousedown = null
    })
  },
  methods: {
    initData() {
      // 获取外包裹元素
      this.barWrap = this.$refs[this.id]
      // 获取滑动条
      this.barCon = this.barWrap.getElementsByClassName('bar-container')[0]
      // 获取滑块
      const eBarDrags = this.barWrap.getElementsByClassName('bar-drag')
      // 获取最大位置
      this.nMax = this.barCon.offsetWidth - eBarDrags[0].offsetWidth
    },
    setDfault() {
      if (isArray(this.data)) {
        const data = this.data
        const numArr = []
        data.forEach(item => {
          numArr.push(item.min, item.max)
        })
        const newData = unique(sort(numArr))
        const arr = []
        newData.forEach(item => {
          if (item !== 0) {
            for (let i = 0; i < data.length; i++) {
              if (item === data[i].max) {
                arr.push({
                  x: item,
                  w:
                    (this.width / this.max) * item === 0
                      ? (this.width / this.max) * item
                      : (this.width / this.max) * item - this.currentWidth / 2,
                  active: false,
                  color: data[i].color || '#ffffff'
                })
                break
              }
            }
          }
        })
        if (arr.length < 1) {
          arr.push({
            x: 0,
            w: 0,
            active: false,
            color: '#ffffff'
          })
        }
        this.bars = arr
        this.$nextTick(() => {
          this.dispatchMouseDown()
        })
      }
    },
    /**
     * @msg: 颜色修改
     * @param {type}
     * @return:
     */
    changeColor() {
      this.change()
    },
    /**
     * @msg: 变化
     * @param {type}
     * @return:
     */
    change() {
      const li = []
      const bars = this.bars
      bars.forEach(bar => {
        li.push(bar.x)
      })
      li.push(this.max)
      li.unshift(0)
      const levelArr = []
      const newData = unique(sort(li))
      newData.forEach((item, index) => {
        if (index) {
          let color = '#ffffff'
          for (let i = 0; i < bars.length; i++) {
            if (bars[i].x === item) {
              color = bars[i].color || '#ffffff'
              break
            }
          }
          levelArr.push({
            min: newData[index - 1],
            max: item,
            level: index - 1,
            color
          })
        }
      })
      this.$emit('changeSuperSlider', levelArr)
    },
    /**
     * @msg: 获取焦点的样式调整
     * @param {type}
     * @return:
     */
    changeStyle(num) {
      this.activedNum = num
      const newData = JSON.parse(JSON.stringify(this.bars))
      newData.forEach((data, index) => {
        if (index === num) {
          data.active = true
        } else {
          data.active = false
        }
      })
      this.bars = newData
    },
    /**
     * @msg:
     * @param {num} 节点编号
     * @return: null
     */
    deleteBar(num) {
      const len = this.bars.length
      if (len <= 1) {
        return
      }
      const newData = JSON.parse(JSON.stringify(this.bars))
      newData.splice(num, 1)

      this.bars = newData
      this.$nextTick(() => {
        this.change()
      })
    },
    /**
     * 绑定鼠标按下事件
     */
    dispatchMouseDown() {
      const that = this
      const eBarDrags = this.$refs['barDrag']
      eBarDrags.forEach((item, index) => {
        item.index = index
        // 滑块添加拖拽事件
        item.onmousedown = function(event) {
          event.stopPropagation()
          that.initData()
          const currentIndex = this.index
          if (currentIndex === that.data.length - 1) {
            return
          }
          that.changeStyle(currentIndex)
          // 初始化鼠标开始拖拽的点击位置
          const nInitX = event.clientX
          // 初始化滑块位置
          const nInitLeft = this.offsetLeft
          // 页面绑定鼠标移动事件
          document.onmousemove = event => {
            event.stopPropagation()
            // 鼠标移动时取消默认行为，避免选中其他元素或文字
            event.preventDefault()
            // 获取鼠标移动后滑块应该移动到的位置
            let nX = event.clientX - nInitX + nInitLeft
            // 限制滑块最大移动位置
            if (nX >= that.nMax) {
              nX = that.nMax
            }
            // 限制滑块最小移动位置
            if (nX <= 0) {
              nX = 0
            }

            this.style.left = nX + 'px'
            const newData = JSON.parse(JSON.stringify(that.bars))
            newData[this.index].x = Number(((that.max / that.nMax) * nX).toFixed(that.pointNum))
            that.bars = newData
          }
          document.onmouseup = event => {
            event.stopPropagation()
            that.change()
            document.onmousemove = null
            document.onmouseup = null
          }
        }
      })
    },
    /**
     * @msg: 双击添加节点
     * @param { null }
     * @return: null
     */
    dispatchDblclick() {
      this.barCon.addEventListener('dblclick', this.handleDblClick)
    },
    handleDblClick() {
      event.stopPropagation()
      this.initData()
      let nLeft = this.barCon.offsetLeft
      // 获取有定位的父元素
      let eParent = this.barCon.offsetParent
      // 循环所有有定位的父元素
      while (eParent) {
        // 添加定位父元素offsetLeft值，用于准确定位滑动条与页面左侧的距离
        nLeft += eParent.offsetLeft
        // 再次获取父元素外的定位父元素
        eParent = eParent.offsetParent
      }
      // 计算滑块位置
      const nX = event.clientX - nLeft
      const tNum =
        ((nX / this.barCon.offsetWidth) * this.max).toFixed(this.pointNum) > this.max
          ? this.max
          : ((nX / this.barCon.offsetWidth) * this.max).toFixed(this.pointNum)
      const wNum = event.offsetX - this.currentWidth / 2
      this.bars = [
        ...this.bars,
        {
          ...this.defaultData,
          w: wNum,
          x: Number(tNum)
        }
      ]
      this.$nextTick(() => {
        this.dispatchMouseDown()
        this.change()
      })
    },
    /**
     * @msg: 键盘事件绑定
     * @param {type}
     * @return:
     */
    dispatchOnKeyUP(e) {
      this.bindKeyDelete(e)
    },
    /**
     * @msg: 键盘 DELETE 时间绑定
     * @param {type}
     * @return:
     */
    bindKeyDelete(e) {
      let keycode = 0
      // eslint-disable-next-line eqeqeq
      if (navigator.appName == 'Microsoft Internet Explorer') {
        keycode = event.keyCode
      } else {
        keycode = e.which
      }
      if (keycode === 46) {
        this.deleteBar(this.activedNum)
      }
    }
  }
}
</script>
<style scoped lang="less">
@deep: ~'>>>';

.super-slider-wrapper {
  position: relative;

  .bar-container {
    height: 10px;
    background: #eee;
    cursor: pointer;
    box-shadow: 0px 1px 3px #ccc;
    border-radius: 10px;
  }

  .bar-into {
    height: 100%;
    width: 0%;
  }

  .bar-drag {
    position: absolute;
    height: 12px;
    width: 12px;
    background: #6dc4ff;
    top: -1px;
    cursor: pointer;
    box-shadow: 0px 0px 3px #6b8797;
    border-radius: 50%;
  }

  .active {
    background: #1475b6;
    box-shadow: 0 0 3px 2px #1475b6;
  }

  .bar-text {
    position: absolute;
    top: 20px;
    left: -15px;
    width: 40px;
    line-height: 24px;
    height: 24px;
    text-align: center;
    background: #f0f0f0;
    box-shadow: 0 1px 2px #aaa;
  }

  .color {
    position: absolute;
    bottom: 20px;
    right: -23px;
  }

  .bar-text::before {
    content: '';
    position: absolute;
    top: -12px;
    left: 14px;
    border: 6px solid transparent;
    border-bottom-color: #f0f0f0;
  }
  .color::after {
    content: '';
    position: absolute;
    bottom: -5px;
    left: 24px;
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-top: 6px solid #ccc;
  }

  @{deep} .ivu-input-type-text .ivu-input {
    height: 27px;
  }
}
</style>
